package com.hontech.bv.conn

import android.os.Handler
import android.util.Log
import com.hontech.bv.conn.serial.SerialType
import com.hontech.bv.conn.serial.Uint8
import com.hontech.bv.conn.usb.UsbSerialPortMod
import com.hontech.bv.error.AckTimeoutException
import com.hontech.bv.error.WaitRespTimeoutException
import com.hontech.bv.task.PromiseTask
import com.hontech.bv.task.TaskThread
import com.hontech.bv.task.await
import com.hontech.bv.utils.USB_PORT_NAME
import com.hontech.bv.utils.log
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.TimeoutCancellationException
import java.io.IOException

private object SerialPortTask : Handler( TaskThread("serial port task").looper )

object SerialPortMod {

    private const val BAUD_RATE = 115200

    private var mPort: SerialPort? = null
    private var mReader: SerialPortReader? = null


    fun isOpen() = mReader != null

    suspend fun open(name: String) {
        mReader?.close()

        val port = if (name == USB_PORT_NAME) {
            UsbSerialPortMod.open()
        } else {
            TTYSerialPort(name, BAUD_RATE)
        }
        val reader = SerialPortReader(port)
        mPort = port
        mReader = reader
    }

    suspend fun close() {
        mReader?.close()
        mPort = null
        mReader = null
    }

    suspend fun simpleRequest(req: Int, dest: Int, args: Array<SerialType>): ReadFrame {
        val wf = WriteFrame(req, dest, args)

        val promise = PromiseTask {
            writeFrame(WriteFrame(req, dest, args))
        }.exec(SerialPortTask).await()

        while (true) {
            try {
                return promise.await(8 * 1000)
            } catch (e: TimeoutCancellationException) {
                log("返回超时发送查询命令:${e.message}")
            }
            val taskInfo = getTaskInfo(wf.dest, 0)
            if ((wf.req != taskInfo.req) ||
                (wf.seq != taskInfo.seq)) {
                return promise.await(500)
            }
        }
    }

    suspend fun request(req: Int, dest: Int, args: Array<SerialType>): ReadFrame {
        preProg3(dest)
        val wf = WriteFrame(req, dest, args)

        val promise = PromiseTask {
            writeFrame3(wf)
        }.exec(SerialPortTask).await()

        while (true) {
            try {
                return promise.await(8 * 1000)
            } catch (e: TimeoutCancellationException) {
                log("返回超时发送查询命令:${e.message}")
            }
            val taskInfo = getTaskInfo(wf.dest, wf.req)
            if ((wf.req != taskInfo.req) ||
                (wf.seq != taskInfo.seq)) {
                return promise.await(500)
            }
        }
    }

    private suspend fun preProg3(dest: Int) {

        var err: Exception? = null

        for (i in 0 until 3) {

            try {
                preProg(dest)
                return
            } catch (e: Exception) {
                err = e
            }
        }

        if (err != null) {
            throw err
        }

    }

    private suspend fun preProg(dest: Int) {
        val frame = WriteFrame(Proto.REQ_PRE_PROCESS, dest, arrayOf())
        val promise = PromiseTask {
            writeFrame(frame)
        }.exec(SerialPortTask).await()
        val result = promise.await(1000)
        result.assert("预处理失败")
    }

    private suspend fun getTaskInfo(dest: Int, taskId: Int): TaskInfo {
        val frame = WriteFrame(Proto.REQ_GET_TASK, dest, arrayOf(Uint8(taskId)))
        val promise = PromiseTask {
            writeFrame(frame)
        }.exec(SerialPortTask).await()
        val result = promise.await(1000)
        val req = Uint8()
        val seq = Uint8()
        result.parse(arrayOf(req, seq))
        return TaskInfo(req.value, seq.value)
    }

    private fun writeFrame3(frame: WriteFrame): CompletableDeferred<ReadFrame> {
        val reader = mReader ?: throw IOException("串口没有打开")
        val port = mPort ?: throw IOException("串口没有打开2")

        for (i in 0 until 3) {
            val promise = reader.begin(frame)
            port.writeFrame(frame)
            val ret = reader.waitAck()
            if (ret) {
                return promise
            }
            Log.e("ack err", "writeFrame3: 没有收到ack")
        }
        throw AckTimeoutException("没有收到ACK ${frame.info()}")
    }

    private fun writeFrame(frame: WriteFrame): CompletableDeferred<ReadFrame> {
        val reader = mReader ?: throw IOException("串口没有打开")
        val port = mPort ?: throw IOException("串口没有打开2")

        val promise = reader.begin(frame)
        port.writeFrame(frame)

        val ret = reader.waitAck()
        if (!ret) {
            throw AckTimeoutException("没有收到ACK ${frame.info()}")
        }
        return promise
    }

}



